package com.lagou.config;

import com.lagou.request.RpcDecoder;
import com.lagou.request.RpcEncoder;
import com.lagou.request.RpcRequest;
import com.lagou.serializer.JSONSerializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Method;

public class Server {

    private ApplicationContext applicationContext;

    //创建一个方法启动服务器
    public void startServer(String ip, int port) throws InterruptedException {
        //1.创建两个线程池对象
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();

        //2.创建服务端的启动引导对象
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        //3.配置启动引导对象
        serverBootstrap.group(bossGroup, workGroup)
                //设置通道为NIO
                .channel(NioServerSocketChannel.class)
                //创建监听channel
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                        //获取管道对象
                        ChannelPipeline pipeline = nioSocketChannel.pipeline();
                        //给管道对象pipeLine 设置编码
//                        pipeline.addLast(new StringEncoder());
//                        pipeline.addLast(new StringDecoder());

                        pipeline.addLast(new RpcDecoder(RpcRequest.class, new JSONSerializer()));
                        pipeline.addLast(new RpcEncoder(String.class, new JSONSerializer()));

                        //把我们自顶一个ChannelHander添加到通道中
                        pipeline.addLast(new ChannelInboundHandlerAdapter() {

                            //当客户端读取数据时,该方法会被调用
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

                                //注意:  客户端将来发送请求的时候会传递一个参数:  UserService#sayHello#are you ok
                                //1.判断当前的请求是否符合规则
                                //        if(msg.toString().startsWith("UserService")){
                                //            //2.如果符合规则,调用实现类货到一个result
                                //            UserServiceImpl service = new UserServiceImpl();
                                //            String result = service.sayHello(msg.toString().substring(msg.toString().lastIndexOf("#")+1));
                                //            //3.把调用实现类的方法获得的结果写到客户端
                                //            ctx.writeAndFlush(result);
                                //        }

                                RpcRequest rpcRequest = (RpcRequest) msg;
                                // 根据类名反射
                                Class<?> clazz = Class.forName(rpcRequest.getClassName());
                                // 获取对象的方法
                                Method method = clazz.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
                                // 从spring容器中获取类型为clazz的bean
                                Object bean = applicationContext.getBean(clazz);
                                // 服务端判断是否以”UserService“开头
                                if (bean.getClass().getSimpleName().indexOf(rpcRequest.getRequestId()) != 0) {
                                    throw new RuntimeException(rpcRequest.getRequestId()+"错误");
                                }
                                // 方法调用
                                Object result = method.invoke(bean, rpcRequest.getParameters());
                                // 结果返回
                                ctx.writeAndFlush(result);

                            }

                        });
                    }
                });

        //4.绑定端口
        serverBootstrap.bind(port).sync();
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}